#include "app_wifi.h"

#include "app_storage.h"

const static char *TAG = "WIFI";
AppWIFI *wifiObj;
TaskHandle_t AppWIFI::taskHandle = NULL;
TimerHandle_t AppWIFI::timerHandle = NULL;

void AppWIFI::init()
{
    this->wifi_netif = NULL;
    this->wifiStatus = WIFIMODE::IDLE;
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    //wifi app
    app_sntp_init();
}

void AppWIFI::loadparameter()
{
    int read_size;

    if(AppStorage::get(cfgPartitionData::ssid,this->ssid,sizeof(this->ssid)) == 0)
    {
        ESP_LOGE("AppStorage", "Get ssid err!");
        snprintf((char *)this->ssid,sizeof(this->ssid),"%s",DEF_WIFI_SSID);
        AppStorage::set(cfgPartitionData::ssid,this->ssid,sizeof(this->ssid));
    }
    if(AppStorage::get(cfgPartitionData::passwd,this->password,sizeof(this->password)) == 0)
    {
        ESP_LOGE("AppStorage", "Get passwd err!");
        snprintf((char *)this->password,sizeof(this->password),"%s",DEF_WIFI_PASSWORD);
        AppStorage::set(cfgPartitionData::passwd,this->password,sizeof(this->password));
    }
}

void AppWIFI::wifi_start()
{

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA();
    esp_netif_config.route_prio = 128;
    esp_netif_t *netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config);
    esp_wifi_set_default_wifi_sta_handlers();
    this->wifi_netif = netif;
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_START, &AppWIFI::wifi_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_STOP, &AppWIFI::wifi_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &AppWIFI::wifi_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &AppWIFI::wifi_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &AppWIFI::ip_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_LOST_IP, &AppWIFI::ip_event_handler, NULL));

#ifdef CONFIG_CONNECT_IPV6
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &AppWIFI::wifi_event_handler, netif));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &AppWIFI::ip_event_handler, NULL));
#endif

    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    wifi_config_t wifi_config = {
        .sta = {
            .scan_method = WIFI_FAST_SCAN,
            .sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
            .threshold = 
                    {
                        .rssi = 0,
                        .authmode = WIFI_AUTH_OPEN,
                    },
        },
    };
    this->getSSID(wifi_config.sta.ssid);
    this->getPassword(wifi_config.sta.password);
    
    ESP_LOGI(TAG, "Connecting to %s[%s]", wifi_config.sta.ssid,wifi_config.sta.password);
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
}


void AppWIFI::wifi_stop()
{
    esp_netif_t *wifi_netif = this->wifi_netif;
    
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_START, &AppWIFI::wifi_event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_STOP, &AppWIFI::wifi_event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &AppWIFI::wifi_event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &AppWIFI::wifi_event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &AppWIFI::ip_event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_LOST_IP, &AppWIFI::ip_event_handler));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
    ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &AppWIFI::wifi_event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &AppWIFI::ip_event_handler));
#endif
    esp_err_t err = esp_wifi_stop();
    if (err == ESP_ERR_WIFI_NOT_INIT) {
        return;
    }
    ESP_ERROR_CHECK(err);
    ESP_ERROR_CHECK(esp_wifi_deinit());
    ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(wifi_netif));
    esp_netif_destroy(wifi_netif);
    this->wifi_netif = NULL;
}


void AppWIFI::wifi_event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    switch (event_id) {
        case WIFI_EVENT_STA_START:
            ESP_LOGI(TAG, "Start event!!!");
            esp_wifi_connect();
            break;
        case WIFI_EVENT_STA_STOP:
            ESP_LOGI(TAG, "Stop event!!!");
            break;
        case WIFI_EVENT_STA_DISCONNECTED:
            ESP_LOGI(TAG, "Disonnect event!!!");
            wifiObj->wifiStatus = WIFIMODE::DISCONNECT;
            break;
        case WIFI_EVENT_STA_CONNECTED:
            ESP_LOGI(TAG, "Connect event!!!");
            wifiObj->wifiStatus = WIFIMODE::CONNECT;
            break;
        default:
            break;
    }
}

void AppWIFI::ip_event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    switch (event_id) 
    {
        case IP_EVENT_STA_GOT_IP:
        {
            ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
            
            if (strncmp(esp_netif_get_desc(wifiObj->wifi_netif),esp_netif_get_desc(event->esp_netif), strlen(esp_netif_get_desc(wifiObj->wifi_netif)) - 1)) 
            {
                ESP_LOGW(TAG, "Got IPv4 from another interface \"%s\": ignored", esp_netif_get_desc(event->esp_netif));
                return;
            }
            ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
            memcpy(&wifiObj->s_ip_addr, &event->ip_info.ip, sizeof(wifiObj->s_ip_addr));
            break;
        }
        case IP_EVENT_STA_LOST_IP:
            ESP_LOGI(TAG, "IP_EVENT_STA_GOT_IP");
        break;

        default:
            break;
    }
}

void AppWIFI::wifiConnect()
{
    this->wifiMode = WIFIMODE::CONNECT;
    doflag = 0;
}

void AppWIFI::wifiDisConnect()
{
    this->wifiMode = WIFIMODE::DISCONNECT;
    doflag = 0;
}


void AppWIFI::doIdle()
{
    vTaskDelay(2); 
}

void AppWIFI::doConnect()
{
    switch(this->doflag)
    {
        case 0 :
            //get the wifi configuration
            this->loadparameter();
            ESP_LOGI(TAG, "Get the wifi configuration");
            this->doflag = 1;
        break;

        case 1:
            this->wifiStatus = WIFIMODE::IDLE;
            this->wifi_start();
            this->doflag = 2;
        break;

        case 2:
            //wait for the result 
            if(this->wifiStatus == WIFIMODE::CONNECT)
            {
                ESP_LOGI(TAG, "WIFI has connected");
                this->wifiMode = WIFIMODE::IDLE;
            }
            else if(this->wifiStatus == WIFIMODE::DISCONNECT)
            {
                this->wifiMode = WIFIMODE::RETRY;
                this->doflag = 0;
            }
        break;
        default:
            this->wifiMode = WIFIMODE::IDLE;
        break;
    }
}

void AppWIFI::doRetry()
{
    char temp;
    switch((this->doflag&0x0f))
    {
        case 0 :
            //will re try
            temp = this->doflag>>4;
            temp++;
            this->doflag = 0x01;
            this->doflag |= (temp<<4);
        break;

        case 1:
            if((this->doflag>>4) >= 10)
            {
                //reconnect err
                ESP_LOGI(TAG, "WIFI reconnect err!");
                this->wifiMode = WIFIMODE::IDLE;
            }
            ESP_LOGI(TAG, "WIFI reconnect[%d]",(this->doflag>>4));
            this->wifiStatus = WIFIMODE::IDLE;
            esp_wifi_connect();
            //wait for result
            this->doflag &= 0xf0;
            this->doflag |= 0x02;
        break;

        case 2:
           if(this->wifiStatus == WIFIMODE::CONNECT)
            {
                ESP_LOGI(TAG, "WIFI has connected");
                this->wifiMode = WIFIMODE::IDLE;
            }
            else if(this->wifiStatus == WIFIMODE::DISCONNECT)
            {
                ESP_LOGI(TAG, "wait for next retry!!!");
                 this->doflag &= 0xf0;
            }
        break;

        default:
            this->wifiMode = WIFIMODE::IDLE;
        break;
    }
}

void AppWIFI::doDisconnect()
{
    
}

void AppWIFI::threadtimeCallback(TimerHandle_t handle)
{
   
}

void AppWIFI::process()
{
    switch(this->wifiMode)
    {
        case WIFIMODE::IDLE :
            this->doIdle();
        break;

        case WIFIMODE::CONNECT :
            this->doConnect();
        break;

        case WIFIMODE::RETRY :
            this->doRetry();
        break;

        case WIFIMODE::DISCONNECT :
            this->doDisconnect();
        break;
    }
}


void AppWIFI::thread(void *a)
{
    wifiObj = new AppWIFI();
    wifiObj->init();
    AppWIFI::timerHandle = xTimerCreate(TAG, pdMS_TO_TICKS(WIFI_TASK_TIME), true, NULL, AppWIFI::threadtimeCallback);
    xTimerStart(AppWIFI::timerHandle,pdMS_TO_TICKS(WIFI_TASK_TIME));
    while(1)
    {
        wifiObj->process();
        vTaskDelay(2); 
    }
}


void AppWIFI::threadCreate()
{
   xTaskCreate(AppWIFI::thread, TAG, WIFI_STACK_SIZE, NULL, WIFI_TASK_PRIO, &(AppWIFI::taskHandle));
}

